package cn.springboot.erp.shiro;

import cn.hutool.crypto.digest.MD5;
import cn.springboot.erp.commons.JWTToken;
import cn.springboot.erp.entity.RolePers;
import cn.springboot.erp.entity.UserRole;
import cn.springboot.erp.mapper.PermissionMapper;
import cn.springboot.erp.mapper.RolePersMapper;
import cn.springboot.erp.mapper.UserRoleMapper;
import cn.springboot.erp.utils.IpUtil;
import cn.springboot.erp.utils.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class CustomerRealm extends AuthorizingRealm {

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private JWTUtil jwtUtil;
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private RolePersMapper rolePersMapper;
    @Resource
    private PermissionMapper permissionMapper;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        /**
         * 获取用户id
         */
        Object id = principalCollection.getPrimaryPrincipal();
        /**
         * 根据用户"id"查询用户角色关联表
         * 可以获得用户具有的角色编号"roleIds"
         */
        LambdaQueryWrapper<UserRole> userRoleLambda = new QueryWrapper<UserRole>().lambda();
        userRoleLambda.eq(UserRole::getUserId,id);
        List<Integer> roleIds = userRoleMapper.selectList(userRoleLambda).stream().map(userRole -> userRole.getRoleId()).collect(Collectors.toList());
        /**
         * 根据角色编号"roleIds"查询角色权限关联表
         * 可以获取用户具有的权限编号"permissionIds"
         */
        LambdaQueryWrapper<RolePers> rolePermissionLambda = new QueryWrapper<RolePers>().lambda();
        rolePermissionLambda.in(RolePers::getRoleId,roleIds);
        Set<Integer> permissionIds = rolePersMapper.selectList(rolePermissionLambda).stream().map(rolePers -> rolePers.getPerId()).collect(Collectors.toSet());
        /**
         * 遍历权限编号"permissionIds"查询权限表
         * 可以获得按钮权限
         */
        List<String> permissionList = permissionMapper.selectBatchIds(permissionIds)
                .stream()
                .filter(permission -> permission.getIsMenu() == 2)
                .map(permission -> permission.getPermission())
                .collect(Collectors.toList());

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissionList);
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        /**
         * 获取主体认证的token
          */

        JWTToken jwtToken = (JWTToken) authenticationToken;
        String tok = (String) jwtToken.getPrincipal();

        // 将tok加密处理
        String token = MD5.create().digestHex16(tok);
        // 判断redis中的token是否过期
        if (!stringRedisTemplate.hasKey(token)){
            throw new RuntimeException("token过期");
        }
        /**
         * 判断token是否被盗用
          */
        // 获取redis中缓存的token
        String redisIp = stringRedisTemplate.opsForValue().get(token);
        // 获取当前主机的ip地址
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String ip = IpUtil.getIp(request);
        // 判断ip是否相同
        if (!StringUtils.equals(redisIp,ip)){
            throw new RuntimeException("token被盗用");
        }

        // 校验token
        jwtUtil.verify(tok);

        // 解析token
        Map<String, Object> claims = jwtUtil.getClaims(tok);
        // 获取载荷中保存的id
        Object id = claims.get("id");

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(id, tok, this.getName());
        return simpleAuthenticationInfo;
    }
}
